use lazy_static::lazy_static;
use std::{
    ffi::{CStr, CString},
    os::raw::c_char,
    sync::{
        Arc, Mutex,
        mpsc::{self, Receiver, Sender},
    },
    thread::{self, JoinHandle},
};
type RustLogStrCb = extern "C" fn(*const c_char);
#[link(name = "ffmpeg")]
unsafe extern "C" {
    fn ffmpeg_main(cmdline_str: *const c_char) -> i32;
    fn ffmpeg_stop();
    fn my_av_log_set_callback(cb: RustLogStrCb);
}
// === 全局异步日志发送器（跨线程共享） ===
lazy_static! {
    static ref GLOBAL_LOG_SENDER: Mutex<Option<Sender<String>>> = Mutex::new(None);
}
// === 同步日志 Hook（仅*当前线程*可用） ===
thread_local! {
    static SYNC_LOG_HOOK: std::cell::RefCell<Option<Arc<Mutex<Vec<String>>>>> = std::cell::RefCell::new(None);
}
pub struct FfmpegController {
    handle: Option<JoinHandle<i32>>,
    sync_logs: Arc<Mutex<Vec<String>>>,
}

impl FfmpegController {
    pub fn new() -> Self {
        Self {
            handle: None,
            sync_logs: Arc::new(Mutex::new(vec![])),
        }
    }
    /// 同步运行 FFmpeg，日志通过 get_logs() 获取
    pub fn run_sync(&mut self, args: Vec<String>) -> i32 {
        let cmd_str = args.join(" ");
        log::info!("run_sync ALL_CMD={},", &cmd_str);
        let cmd = CString::new(cmd_str).unwrap();
        //清空日志
        self.sync_logs.lock().unwrap().clear();

        let logs = self.sync_logs.clone();
        SYNC_LOG_HOOK.with(|hook| {
            *hook.borrow_mut() = Some(logs);
        });

        extern "C" fn sync_log_callback(msg: *const c_char) {
            unsafe {
                if !msg.is_null() {
                    if let Ok(s) = CStr::from_ptr(msg).to_str() {
                        SYNC_LOG_HOOK.with(|hook| {
                            if let Some(ref vec) = *hook.borrow() {
                                let s = s.trim_end().to_string();
                                //log::info!("{}", &s);
                                vec.lock().unwrap().push(s);
                            }
                        });
                    }
                }
            }
        }
        let r;
        unsafe {
            my_av_log_set_callback(sync_log_callback);

            r = ffmpeg_main(cmd.as_ptr());
        }

        SYNC_LOG_HOOK.with(|hook| {
            *hook.borrow_mut() = None;
        });
        return r;
    }

    pub fn get_logs(&self) -> Vec<String> {
        self.sync_logs.lock().unwrap().clone()
    }

    /// 异步运行 FFmpeg，返回控制通道和日志接收器
    pub fn run_async(&mut self, args: Vec<String>) -> Receiver<String> {
        let cmd_str = args.join(" ");
        println!("ALL_CMD={},", &cmd_str);
        let cmd = CString::new(cmd_str).unwrap();
        let (log_tx, log_rx) = mpsc::channel::<String>();

        // 设置全局日志发送器
        // 复制了一个 Sender 给日志回调线程用，但这个 Sender 不清空就永远“持有通道”。
        // Rust 的 Receiver 在 for log in log_rx 中只有在 所有发送者全部销毁 时，才会退出
        *GLOBAL_LOG_SENDER.lock().unwrap() = Some(log_tx.clone());

        extern "C" fn async_log_callback(msg: *const c_char) {
            unsafe {
                if !msg.is_null() {
                    if let Ok(s) = CStr::from_ptr(msg).to_str() {
                        if let Some(ref tx) = *GLOBAL_LOG_SENDER.lock().unwrap() {
                            let _ = tx.send(s.trim_end().to_string());
                        }
                    }
                }
            }
        }

        unsafe {
            my_av_log_set_callback(async_log_callback);
        }

        self.handle = Some(thread::spawn(move || {
            let r;
            unsafe {
                r = ffmpeg_main(cmd.as_ptr());
            }
            // 上边复制了一个 清空全局 sender，防止它阻止日志线程退出
            *GLOBAL_LOG_SENDER.lock().unwrap() = None;
            drop(log_tx); // 关闭日志发送器
            println!("Async ffmpeg thread exiting");
            return r;
        }));

        log_rx
    }

    pub fn wait_stop(&mut self) -> i32 {
        //println!("Received stop signal, calling ffmpeg_stop()");
        unsafe { ffmpeg_stop() };
        if let Some(h) = self.handle.take() {
            let r = h.join();
            return r.unwrap();
        }
        //println!("Called ffmpeg_stop()");
        return 0;
    }
}
